{-
    pHaskell Translator v0.1 
    Copyright (C) 2009  Ruben Marcelo Caro 

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-}
module LambdaLifting where
import LambdaTypes
 
{- 
Por cada definicion debemos retornar un par 
(Definicion Post lifting, definiciones locales a agregar)
-}
lambdaLifting::LDefinition->LDefinition
lambdaLifting (LDefinition name e locals) = LDefinition name e' (locals++locals')
											where
												(e', locals') = searchToMainExpAnReplase e 

{- 
Toma una exprecion, le aplica lambda-lifting
y debuelve la expresion modificada y las definiciones locales
que resuelven el lambda
va iterando por lambdas hasta que encuentra algo distinto y pasa
a extractLambda con un ID inicial para que remplace todas las
lambdas
-}
searchToMainExpAnReplase::LExp->(LExp, LProgram)
searchToMainExpAnReplase (LLambda var lType e) = (LLambda var lType e', locals)
										where
											(e',locals) = searchToMainExpAnReplase e
searchToMainExpAnReplase e                              = extractLambda e "_"

{- 
Toma una exprecion y un id, luego busca  recursivamente y si encuentra
una lambda la reemplaza remplaza 
 -}
extractLambda::LExp->String->(LExp, LProgram)
extractLambda lambda@(LLambda var lType e) sid = replaceLambda lambda sid 
														
extractLambda (LApp e e') sid = ((LApp ee ee'), elocals++elocals')
										where
											(ee,  elocals) = extractLambda  e (sid++"appL")
											(ee', elocals') = extractLambda  e' (sid++"appR")
extractLambda e@(LInteger n)  sid = (e, [])
extractLambda e@(LVariable v) sid = (e, [])
extractLambda e@(LBoolean b)  sid = (e, [])   

extractLambda (LIf e e' e'') sid = ((LIf ee ee' ee''), elocals++elocals'++elocals'')
										where
											(ee,  elocals)    = extractLambda  e   (sid++"if")
											(ee', elocals')   = extractLambda  e'  (sid++"then")
											(ee'', elocals'') = extractLambda  e'' (sid++"else")

extractLambda (LIfOnly e e') sid = ((LIfOnly ee ee'), elocals++elocals')
										where
											(ee,  elocals)    = extractLambda  e   (sid++"if")
											(ee', elocals')   = extractLambda  e'  (sid++"then")
											
{-
Toma una lambda que debe ser reemplazada y debuelve la variable que 
la remplazara y genera loas definiciones locales correspondiente
-}											
replaceLambda::LExp->String->(LExp, LProgram)
replaceLambda lambda@(LLambda bindedVar lType e) sid = ((LVariable newName), locals')
						 where
							newName          = "lambdaLifting_" ++ sid
							locals'          = [(LDefinition newName (LLambda bindedVar lType ee)  elocals)]
							(ee ,  elocals ) = extractLambda e (sid++"lmRL") 
																


						
 
  